/*
 *  clb_device_info.c
 *  clbench
 *
 *  Created by Holmes Futrell on 12/16/09.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.
 *
 */

#include "clb_device_info.h"
#include "clb_error.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
	
void getDeviceInfo(cl_device_id device) {
	
	print_device_info_cl_device_type(device, "CL_DEVICE_TYPE", CL_DEVICE_TYPE);
	print_device_info_cl_uint(device, "CL_DEVICE_VENDOR_ID", CL_DEVICE_VENDOR_ID);
	print_device_info_cl_uint(device, "CL_DEVICE_MAX_COMPUTE_UNITS", CL_DEVICE_MAX_COMPUTE_UNITS);
	print_device_info_cl_uint(device, "CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS", CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS);
	print_device_info_size_t_array(device, "CL_DEVICE_MAX_WORK_ITEM_SIZES", CL_DEVICE_MAX_WORK_ITEM_SIZES);
	print_device_info_size_t(device, "CL_DEVICE_MAX_WORK_GROUP_SIZE", CL_DEVICE_MAX_WORK_GROUP_SIZE);
	print_device_info_cl_uint(device, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR", CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR);
	print_device_info_cl_uint(device, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT", CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT);
	print_device_info_cl_uint(device, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT", CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT);
	print_device_info_cl_uint(device, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG", CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG);
	print_device_info_cl_uint(device, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT", CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT);
	print_device_info_cl_uint(device, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE", CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE);
	print_device_info_cl_uint(device, "CL_DEVICE_MAX_CLOCK_FREQUENCY", CL_DEVICE_MAX_CLOCK_FREQUENCY);
	print_device_info_cl_uint(device, "CL_DEVICE_ADDRESS_BITS", CL_DEVICE_ADDRESS_BITS);
	print_device_info_cl_ulong(device, "CL_DEVICE_MAX_MEM_ALLOC_SIZE", CL_DEVICE_MAX_MEM_ALLOC_SIZE);
	print_device_info_cl_bool(device, "CL_DEVICE_IMAGE_SUPPORT", CL_DEVICE_IMAGE_SUPPORT);
	print_device_info_cl_uint(device, "CL_DEVICE_MAX_READ_IMAGE_ARGS", CL_DEVICE_MAX_READ_IMAGE_ARGS);
	print_device_info_cl_uint(device, "CL_DEVICE_MAX_WRITE_IMAGE_ARGS", CL_DEVICE_MAX_WRITE_IMAGE_ARGS);
	print_device_info_size_t(device, "CL_DEVICE_IMAGE2D_MAX_WIDTH", CL_DEVICE_IMAGE2D_MAX_WIDTH);
	print_device_info_size_t(device, "CL_DEVICE_IMAGE2D_MAX_HEIGHT", CL_DEVICE_IMAGE2D_MAX_HEIGHT);
	print_device_info_size_t(device, "CL_DEVICE_IMAGE3D_MAX_WIDTH", CL_DEVICE_IMAGE3D_MAX_WIDTH);
	print_device_info_size_t(device, "CL_DEVICE_IMAGE3D_MAX_HEIGHT", CL_DEVICE_IMAGE3D_MAX_HEIGHT);
	print_device_info_size_t(device, "CL_DEVICE_IMAGE3D_MAX_DEPTH", CL_DEVICE_IMAGE3D_MAX_DEPTH);
	print_device_info_cl_uint(device, "CL_DEVICE_MAX_SAMPLERS", CL_DEVICE_MAX_SAMPLERS);
	print_device_info_size_t(device, "CL_DEVICE_MAX_PARAMETER_SIZE", CL_DEVICE_MAX_PARAMETER_SIZE);
	print_device_info_cl_uint(device, "CL_DEVICE_MEM_BASE_ADDR_ALIGN", CL_DEVICE_MEM_BASE_ADDR_ALIGN);
	print_device_info_cl_uint(device, "CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE", CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE);
	print_device_info_cl_device_fp_config(device, "CL_DEVICE_SINGLE_FP_CONFIG", CL_DEVICE_SINGLE_FP_CONFIG);
	print_device_info_cl_device_mem_cache_type(device, "CL_DEVICE_GLOBAL_MEM_CACHE_TYPE", CL_DEVICE_GLOBAL_MEM_CACHE_TYPE);
	print_device_info_cl_uint(device, "CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE", CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE);
	print_device_info_cl_ulong(device, "CL_DEVICE_GLOBAL_MEM_CACHE_SIZE", CL_DEVICE_GLOBAL_MEM_CACHE_SIZE);
	print_device_info_cl_ulong(device, "CL_DEVICE_GLOBAL_MEM_SIZE", CL_DEVICE_GLOBAL_MEM_SIZE);
	print_device_info_cl_ulong(device, "CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE", CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE);
	print_device_info_cl_uint(device, "CL_DEVICE_MAX_CONSTANT_ARGS", CL_DEVICE_MAX_CONSTANT_ARGS);
	print_device_info_cl_device_local_mem_type(device, "CL_DEVICE_LOCAL_MEM_TYPE", CL_DEVICE_LOCAL_MEM_TYPE);
	print_device_info_cl_ulong(device, "CL_DEVICE_LOCAL_MEM_SIZE", CL_DEVICE_LOCAL_MEM_SIZE);
	print_device_info_cl_bool(device, "CL_DEVICE_ERROR_CORRECTION_SUPPORT", CL_DEVICE_ERROR_CORRECTION_SUPPORT);
	print_device_info_size_t(device, "CL_DEVICE_PROFILING_TIMER_RESOLUTION", CL_DEVICE_PROFILING_TIMER_RESOLUTION);
	print_device_info_cl_bool(device, "CL_DEVICE_ENDIAN_LITTLE", CL_DEVICE_ENDIAN_LITTLE);
	print_device_info_cl_bool(device, "CL_DEVICE_AVAILABLE", CL_DEVICE_AVAILABLE);
	print_device_info_cl_bool(device, "CL_DEVICE_COMPILER_AVAILABLE", CL_DEVICE_COMPILER_AVAILABLE);
	print_device_info_cl_device_exec_capabilities(device, "CL_DEVICE_EXECUTION_CAPABILITIES", CL_DEVICE_EXECUTION_CAPABILITIES);
	print_device_info_cl_command_queue_properties(device, "CL_DEVICE_QUEUE_PROPERTIES", CL_DEVICE_QUEUE_PROPERTIES);
	print_device_info_cl_platform_id(device, "CL_DEVICE_PLATFORM", CL_DEVICE_PLATFORM);
	print_device_info_string(device, "CL_DEVICE_NAME", CL_DEVICE_NAME);
	print_device_info_string(device, "CL_DEVICE_VENDOR", CL_DEVICE_VENDOR);
	print_device_info_string(device, "CL_DRIVER_VERSION", CL_DRIVER_VERSION);
	print_device_info_string(device, "CL_DEVICE_PROFILE", CL_DEVICE_PROFILE);
	print_device_info_string(device, "CL_DEVICE_VERSION", CL_DEVICE_VERSION);
	print_device_info_string(device, "CL_DEVICE_EXTENSIONS", CL_DEVICE_EXTENSIONS);
	printf("\n");
}

void print_device_info_string(cl_device_id device, const char *string, cl_device_info param_name) {
	char buffer[2048];
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(buffer), buffer, &size) );
	if (strlen(buffer) != 0) {
		printf("%s: '%s'\n", string, buffer);
	}
}

void print_device_info_cl_uint(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_uint val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	printf("%s: %u\n", string, val);
}

void print_device_info_cl_platform_id(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_platform_id val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	printf("%s: %llu\n", string, (uint64_t)val);
}


void print_device_info_cl_ulong(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_ulong val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	printf("%s: %llu\n", string, (unsigned long long)val);
}

void print_device_info_size_t(cl_device_id device, const char *string, cl_device_info param_name) {
	size_t val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	printf("%s: %llu\n", string, (uint64_t)val);
}

void print_device_info_size_t_array(cl_device_id device, const char *string, cl_device_info param_name) {
	size_t val[255];
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	int i, iterations = size / sizeof(size_t);
	printf("%s: ", string);
	for (i=0; i<iterations; i++) {
		printf("%llu", (uint64_t)val[i]);
		if (i != iterations-1) {
			printf(", ");
		}
	}
	printf("\n");
}

void print_device_info_cl_bool(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_bool val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	printf("%s: %s\n", string, val ? "CL_TRUE" : "CL_FALSE");
}

void print_device_info_cl_device_type(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_device_type type;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(type), &type, &size) );
	printf("%s: ", string);
	if ( type & CL_DEVICE_TYPE_CPU ) {
		printf("CL_DEVICE_TYPE_CPU ");
	}
	if ( type & CL_DEVICE_TYPE_GPU) {
		printf("CL_DEVICE_TYPE_GPU ");
	}
	if ( type & CL_DEVICE_TYPE_ACCELERATOR ) {
		printf("CL_DEVICE_TYPE_ACCELERATOR ");
	}
	if ( type & CL_DEVICE_TYPE_DEFAULT ) {
		printf("CL_DEVICE_TYPE_DEFAULT ");
	}
	printf("\n");
}

void print_device_info_cl_device_fp_config(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_device_fp_config val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	
	printf("%s: ", string);
	if ( val & CL_FP_DENORM ) {
		printf("CL_FP_DENORM ");
	}
	if ( val & CL_FP_INF_NAN) {
		printf("CL_FP_INF_NAN ");
	}
	if ( val & CL_FP_ROUND_TO_NEAREST) {
		printf("CL_FP_ROUND_TO_NEAREST ");
	}
	if ( val & CL_FP_ROUND_TO_ZERO ) {
		printf("CL_FP_ROUND_TO_ZERO ");
	}
	printf("\n");
}

void print_device_info_cl_device_mem_type(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_device_fp_config val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	if ( val == CL_LOCAL ) {
		printf("%s: CL_LOCAL\n", string);
	}
	else if ( val == CL_GLOBAL ) {
		printf("%s: CL_GLOBAL\n", string);
	}
	else {
		printf("%s: [UNKNOWN]\n", string);
	}
}

void print_device_info_cl_device_mem_cache_type(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_device_mem_cache_type val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	if ( val == CL_NONE ) {
		printf("%s: CL_NONE\n", string);
	}
	else if ( val == CL_READ_ONLY_CACHE ) {
		printf("%s: CL_READ_ONLY_CACHE\n", string);
	}
	else if (val == CL_READ_WRITE_CACHE ) {
		printf("%s: CL_READ_WRITE_CACHE\n", string);
	}
	else {
		printf("%s: [UNKNOWN]\n", string);
	}
}

void print_device_info_cl_device_local_mem_type(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_device_local_mem_type val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	if ( val == CL_LOCAL ) {
		printf("%s: CL_LOCAL\n", string);
	}
	else if ( val == CL_GLOBAL ) {
		printf("%s: CL_GLOBAL\n", string);
	}
	else {
		printf("%s: [UNKNOWN]\n", string);
	}
}

void print_device_info_cl_command_queue_properties(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_command_queue_properties val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	printf("%s: ", string);
	if ( val & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE ) {
		printf("CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE ", string);
	}
	if ( val & CL_QUEUE_PROFILING_ENABLE ) {
		printf("CL_QUEUE_PROFILING_ENABLE ", string);
	}
	printf("\n");
}

void print_device_info_cl_device_exec_capabilities(cl_device_id device, const char *string, cl_device_info param_name) {
	cl_device_exec_capabilities val;
	size_t size;
	clb_check_error_simple( clGetDeviceInfo(device, param_name, sizeof(val), &val, &size) );
	printf("%s: ", string);
	if ( val & CL_EXEC_KERNEL ) {
		printf("CL_EXEC_KERNEL ", string);
	}
	if ( val & CL_EXEC_NATIVE_KERNEL ) {
		printf("CL_EXEC_NATIVE_KERNEL ", string);
	}
	printf("\n");
}